package tool

import (
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v5"
	"guard/internal/conf"
	"time"
)

var (
	/**
	  私钥（无密码）：openssl genrsa -out rsa_private.key 512/1024/2048
	  公钥：openssl rsa -in rsa_private.key -pubout -out rsa_public.key
	  目前用的1024，PKCS1，512长度不够，2048更安全，4096也更安全，但token更长
	*/
	rsaPublic  = conf.Conf.Jwt.RsaPublic
	rsaPrivate = conf.Conf.Jwt.RsaPrivate
)

// 2023/2/24：完全遵循OAuth2.1

type JClaims struct {
	Subject   string   `json:"sub,omitempty"`
	Audience  string   `json:"aud,omitempty"`
	Scope     []string `json:"scope,omitempty"` // 范围
	IssUri    string   `json:"iss,omitempty"`
	CreateAt  int64    `json:"iat,omitempty"`
	ExpiresAt int64    `json:"exp,omitempty"`
	jwt.RegisteredClaims
}

// createJwt 创建jwt
func createJwt(audClientId, subUsername, issUri string, scope []string, expExpiresTime int64) (token string) {

	// 创建jwt信息
	claims := &JClaims{}
	// 用户名
	claims.Subject = subUsername
	// 拥有者
	claims.Audience = audClientId
	// 范围
	claims.Scope = scope
	// 颁发uri
	claims.IssUri = issUri
	// 创建时间
	claims.CreateAt = time.Now().Unix()
	// 过期时间，必须设置，在什么时间过期
	claims.ExpiresAt = expExpiresTime

	// 使用rs256 + claims 生成信息
	tokenJwt := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	// 解析private rsa，解析为结构体
	privateKey, _ := jwt.ParseRSAPrivateKeyFromPEM([]byte(rsaPrivate))
	// 加密，得到token
	token, _ = tokenJwt.SignedString(privateKey)
	return
}

// parseJwt 解析jwt
func parseJwt(tokenString string) (*JClaims, error) {
	// 解析对象
	token, err := jwt.ParseWithClaims(tokenString, &JClaims{},
		// 闭包的作用在于解析公钥
		func(token *jwt.Token) (interface{}, error) {
			// 判断是否是某种加密
			if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("key is errors，签名异常")
			}
			// 返回公钥
			return jwt.ParseRSAPublicKeyFromPEM([]byte(rsaPublic))
		})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*JClaims); ok && token.Valid {
		return claims, nil
	} else {
		return nil, err
	}
}

// Valid 重写token的验证
func (J JClaims) Valid() error {
	unix := time.Now().Unix()

	if J.ExpiresAt < unix {
		return errors.New("令牌已过期")
	}
	return nil
}
